#include "gtest/gtest.h"
#include <cassert>

#include "Appender.h"
#include "Reader.h"
#include "Meta.h"

class StreamTest : public testing::Test {
public:
    void SetUp() override{
        stream_ = new Stream(name) ;
    }
    void TearDown() override {
        assert(stream_ != nullptr);
        stream_->destroy();
        delete stream_;
    }
protected:
    Stream * stream_ = nullptr;
    const char * name = "stream-test.db";

};
TEST_F(StreamTest, testMain){
    Appender appender(stream_);

    ASSERT_TRUE(appender.append("NewGTP1.0").ok());
    ASSERT_EQ(1ull, appender.nextSequenceNumber());
    appender.append("NewGTP1.1");
    appender.append("NewGTP1.2");
    appender.append("NewGTP1.3");
    appender.append("NewGTP1.4");
    ASSERT_EQ(5ull, appender.nextSequenceNumber());

    ASSERT_EQ(5ull, stream_->size().value());
	

    Reader reader(stream_);
    ASSERT_EQ(0, reader.sequenceNumber());

    while(reader.hasNext()){
		auto && current = reader.current();
        ASSERT_EQ(reader.sequenceNumber(), current.first);
        reader.next();
    }
    ASSERT_EQ(reader.sequenceNumber(), stream_->size().value());
}

TEST_F(StreamTest, testPositionAppender){
    {
        Appender appender(stream_);

        ASSERT_TRUE(appender.append("NewGTP1.0").ok());
        ASSERT_EQ(1ull, appender.nextSequenceNumber());
        appender.append("NewGTP1.1");
        appender.append("NewGTP1.2");
        appender.append("NewGTP1.3");
        appender.append("NewGTP1.4");
        ASSERT_EQ(5ull, appender.nextSequenceNumber());

        ASSERT_EQ(5ull, stream_->size().value());
    }
    {
        Appender appender(stream_); // automatical -> seek the size

        ASSERT_TRUE(appender.append("NewGTP1.5").ok());
        ASSERT_EQ(6ull, appender.nextSequenceNumber());
        appender.append("NewGTP1.6");
        appender.append("NewGTP1.7");
        appender.append("NewGTP1.8");
        appender.append("NewGTP1.9");
        ASSERT_EQ(10ull, appender.nextSequenceNumber());

        ASSERT_EQ(10ull, stream_->size().value());
    }
    {
        Appender appender(stream_); // automatical -> seek the size

        ASSERT_TRUE(appender.append("NewGTP1.10").ok());
        ASSERT_EQ(11ull, appender.nextSequenceNumber());
        appender.append("NewGTP1.11");
        appender.append("NewGTP1.12");
        appender.append("NewGTP1.13");
        appender.append("NewGTP1.14");
        ASSERT_EQ(15ull, appender.nextSequenceNumber());

        ASSERT_EQ(15ull, stream_->size().value());
    }
}


TEST_F(StreamTest, testPositionReader){
    uint64_t maxRecordCount = 0;
    {
        Appender appender(stream_);

        ASSERT_TRUE(appender.append("NewGTP1.0").ok());
        ASSERT_EQ(1ull, appender.nextSequenceNumber());
        appender.append("NewGTP1.1");
        appender.append("NewGTP1.2");
        appender.append("NewGTP1.3");
        appender.append("NewGTP1.4");
        ASSERT_EQ(5ull, appender.nextSequenceNumber());

        ASSERT_EQ(5ull, stream_->size().value());
        maxRecordCount = stream_->size().value();
    }
    {  // from start
        Reader reader(stream_, 0); //case 1 
        uint64_t iterCount = 0; 
        while(reader.hasNext()){
            auto && current = reader.current();
            ASSERT_EQ(reader.sequenceNumber(), current.first);
            ++iterCount;
            reader.next();
        }
        ASSERT_EQ(maxRecordCount, iterCount);
        ASSERT_EQ(reader.sequenceNumber(), stream_->size().value());
    }
    { // in the middle of the data file
        uint64_t startSeq = 3;
        Reader reader(stream_, startSeq); //case 2
        ASSERT_EQ(reader.sequenceNumber(), startSeq);

        uint64_t iterCount = 0;
        while(reader.hasNext()){
            auto && current = reader.current();
            ASSERT_EQ(reader.sequenceNumber(), current.first);

            ++iterCount ;
            reader.next();
        }
        ASSERT_EQ(maxRecordCount - startSeq , iterCount);
        ASSERT_EQ(reader.sequenceNumber(), stream_->size().value());
    }

    { // out of data file
        Reader reader(stream_, 10); //case 3
        ASSERT_EQ(reader.sequenceNumber(), 5);

        uint64_t iterCount = 0;
        while(reader.hasNext()){
            auto && current = reader.current();
            ASSERT_EQ(reader.sequenceNumber(), current.first);

            ++iterCount;
            reader.next();
        }
        ASSERT_EQ(0ull, iterCount);
    }
}
